/**
 * 
 */
package com.thon.security;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
import org.apache.shiro.web.util.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author manpiaoyi
 * @mail manpiaoyi@126.com
 * @date 2014-10-16
 * @description TODO
 */
public class ApiBasicHttpAuthenticationFilter extends BasicHttpAuthenticationFilter {
	
	private static final Logger log = LoggerFactory.getLogger(ApiBasicHttpAuthenticationFilter.class);
	
	
	protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) {
        String authorizationHeader = getAuthzHeader(request);
        if (authorizationHeader == null || authorizationHeader.length() == 0) {
            // Create an empty authentication token since there is no
            // Authorization header.
            return createToken("", "", request, response);
        }

        if (log.isDebugEnabled()) {
            log.debug("Attempting to execute login with headers [" + authorizationHeader + "]");
        }

        String[] prinCred = getPrincipalsAndCredentials(authorizationHeader, request);
        if (prinCred == null || prinCred.length < 2) {
            // Create an authentication token with an empty password,
            // since one hasn't been provided in the request.
            String username = prinCred == null || prinCred.length == 0 ? "" : prinCred[0];
            return createToken(username, "", request, response);
        }

        String username = prinCred[0];
        String password = prinCred[1];
        String captcha = "";
        String authcType = AuthcType.BASIC;
		boolean rememberMe = isRememberMe(request);
		String host = getHost(request);

        return new CaptchaUsernamePasswordToken(username, password, rememberMe, host, captcha, authcType);
    }
	
	  protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
	        boolean loggedIn = false; //false by default or we wouldn't be in this method
	        if (isLoginAttempt(request, response)) {
	            loggedIn = executeLogin(request, response);
	        }
	        if (!loggedIn) {
	            sendChallenge(request, response);
	        }
	        return loggedIn;
	    }
	  
	  protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
	        AuthenticationToken token = createToken(request, response);
	        if (token == null) {
	            String msg = "createToken method implementation returned null. A valid non-null AuthenticationToken " +
	                    "must be created in order to execute a login attempt.";
	            throw new IllegalStateException(msg);
	        }
	        try {
	            Subject subject = getSubject(request, response);
	            subject.login(token);
	            return onLoginSuccess(token, subject, request, response);
	        } catch (AuthenticationException e) {
	            return onLoginFailure(token, e, request, response);
	        }
	    }
	  protected boolean onLoginSuccess(AuthenticationToken token, Subject subject,			  
			              ServletRequest request, ServletResponse response) throws Exception {			  
			          //issueSuccessRedirect(request, response);			  
			          //we handled the success redirect directly, prevent the chain from continuing:
			  
			          HttpServletRequest httpServletRequest = (HttpServletRequest)request;			  
			          HttpServletResponse httpServletResponse = (HttpServletResponse)response;			  
			            
			  
			          /*if (!"XMLHttpRequest".equalsIgnoreCase(httpServletRequest.getHeader("X-Requested-With"))			  
			                  || request.getParameter("ajax") == null) {// 不是ajax请求			  
			              httpServletResponse.sendRedirect(httpServletRequest.getContextPath() + this.getSuccessUrl());
			  
			          } else {			  
			              httpServletRequest.getRequestDispatcher("/login/timeout/success").forward(httpServletRequest, httpServletResponse);
			  
			          }*/
			            
			          httpServletResponse.sendRedirect(httpServletRequest.getRequestURL().toString());
			          return false;
			  
			      }
			  
			  

	    protected boolean isLoginAttempt(ServletRequest request, ServletResponse response) {
	        String authzHeader = getAuthzHeader(request);
	        return authzHeader != null && isLoginAttempt(authzHeader);
	    }	   
	    

	    protected String getAuthzHeader(ServletRequest request) {
	        HttpServletRequest httpRequest = WebUtils.toHttp(request);
	        return httpRequest.getHeader(AUTHORIZATION_HEADER);
	    }

	  
	    protected boolean isLoginAttempt(String authzHeader) {
	        String authzScheme = getAuthzScheme().toLowerCase();
	        return authzHeader.toLowerCase().startsWith(authzScheme);
	    }

	    protected boolean sendChallenge(ServletRequest request, ServletResponse response) {
	        if (log.isDebugEnabled()) {
	            log.debug("Authentication required: sending 401 Authentication challenge response.");
	        }
	        HttpServletResponse httpResponse = WebUtils.toHttp(response);
	        httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
	        String authcHeader = getAuthcScheme() + " realm=\"" + getApplicationName() + "\"";
	        httpResponse.setHeader(AUTHENTICATE_HEADER, authcHeader);
	        return false;
	    }	  

	    protected String[] getPrincipalsAndCredentials(String authorizationHeader, ServletRequest request) {
	        if (authorizationHeader == null) {
	            return null;
	        }
	        String[] authTokens = authorizationHeader.split(" ");
	        if (authTokens == null || authTokens.length < 2) {
	            return null;
	        }
	        return getPrincipalsAndCredentials(authTokens[0], authTokens[1]);
	    }

	    protected String[] getPrincipalsAndCredentials(String scheme, String encoded) {
	        String decoded = Base64.decodeToString(encoded);
	        return decoded.split(":", 2);
	    }
}
